package generator;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import generator.model.Table;
import generator.util.FileHelper;
import generator.util.IOHelper;
import generator.util.StringTemplate;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.io.FileUtils;

/**
 * 
 * @author badqiu
 * @email badqiu(a)gmail.com
 */
public class Generator {
	private static final String WEBAPP_GENERATOR_INSERT_LOCATION = "webapp-generator-insert-location";
	private static final String WEBAPP_GENERATOR_INSERT_BEGIN_LOCATION = "<!--"
			+ WEBAPP_GENERATOR_INSERT_LOCATION + ":begin-->";
	private static final String WEBAPP_GENERATOR_INSERT_END_LOCATION = "<!--"
			+ WEBAPP_GENERATOR_INSERT_LOCATION + ":end-->";

	private static final String TEMPFILEPATH = "c:\\temp\\";// 临时文件存放位置
	private static final String TEMPLATEPATH = "template_dao_vo";// 模板根目录路径

	public Generator() {
	}

	public void generateAllTable() throws Exception {
		generateAllTable(new String[] {});
	}

	public void generateAllTable(String[] ignoreTables) throws Exception {
		List tables = DbModelProvider.getInstance().getAllTables();
		int leng = ignoreTables.length;
		boolean isGenerate;
		for (int j = 0; j < tables.size(); j++) {
			isGenerate = true;
			String tableName = ((Table) tables.get(j)).getSqlName().toLowerCase();
			for (int i = 0; i < leng; i++) {
				if (ignoreTables[i].toLowerCase().equals(tableName)) {
					isGenerate = false;
				}
			}
			if (isGenerate) {
				generateTable(tableName);
			}
		}

		DbModelProvider.getInstance().closeConnection();
	}

	public void generateTables(String[] tableNames) throws Exception {
		if (tableNames != null && tableNames.length > 0) {
			for (String tableName : tableNames) {
				generateTable(tableName);
			}
		}

		DbModelProvider.getInstance().closeConnection();
	}

	public void generateTable(String tableName) throws Exception {
		Table table = DbModelProvider.getInstance().getTable(tableName);
		generateTable(table);
	}

	private void generateTable(Table table) throws IOException,
			IllegalAccessException, InvocationTargetException,
			NoSuchMethodException, TemplateException {
		System.out
				.println("***************************************************************");
		System.out.println("* BEGIN generate table:" + table.getSqlName());
		System.out
				.println("***************************************************************");
		Configuration config = new Configuration();
		File templateRootDir = new File(TEMPLATEPATH).getAbsoluteFile();
		config.setDirectoryForTemplateLoading(templateRootDir);
		config.setNumberFormat("###############");
		config.setBooleanFormat("true,false");

		List files = new ArrayList();
		FileHelper.listFiles(templateRootDir, files);

		for (int i = 0; i < files.size(); i++) {
			File file = (File) files.get(i);
			if (file.getName().indexOf("service-context.xml") != -1) {
				System.out.println("");
			}
			String templateRelativePath = FileHelper.getRelativePath(
					templateRootDir, file);
			String outputFilePath = templateRelativePath;
			if (file.isDirectory() || file.isHidden())
				continue;
			if (templateRelativePath.trim().equals(""))
				continue;
			if (file.getName().toLowerCase().endsWith(".include")) {
				System.out.println("[skip]\t\t endsWith '.include' template:"
						+ templateRelativePath);
				continue;
			}
			int testExpressionIndex = -1;
			if ((testExpressionIndex = templateRelativePath.indexOf('@')) != -1) {
				outputFilePath = templateRelativePath.substring(0,
						testExpressionIndex);
				String testExpressionKey = templateRelativePath
						.substring(testExpressionIndex + 1);
				Map map = getFilepathDataModel(table);
				Object expressionValue = map.get(testExpressionKey);
				if (!"true".equals(expressionValue.toString())) {
					System.out.println("[not-generate]\t test expression '@"
							+ testExpressionKey + "' is false,template:"
							+ templateRelativePath);
					continue;
				}
			}
			try {
				generateFile(table, config, templateRelativePath,
						outputFilePath);
			} catch (Exception e) {
				throw new RuntimeException("generate table '"
						+ table.getSqlName() + "' oucur error,template is:"
						+ templateRelativePath, e);
			}
		}
	}

	private void generateFile(Table table, Configuration config,
			String templateRelativePath, String outputFilePath)
			throws IOException, IllegalAccessException,
			InvocationTargetException, NoSuchMethodException, TemplateException {
		Template template = config.getTemplate(templateRelativePath);

		String targetFilename = getTargetFilename(table, outputFilePath);

		Map templateDataModel = getTemplateDataModel(table);
		File absoluteOutputFilePath = getAbsoluteOutputFilePath(targetFilename);
		if (absoluteOutputFilePath.exists()) {// 鏂囦欢瀛樺湪,鏍规嵁鏍囪瘑鍒ゆ柇鏄惁瑕佸線閲岄潰鎻掑叆鍐呭
		/*-----------------------------------鍘熺増鏈彧鏄妸鏁翠釜鏂囦欢鐨勫唴瀹逛笉鏂彃鍏ユ柊鐨勬枃浠惰�宸�-----------------------------------------------*/
			// StringWriter newFileContentCollector = new StringWriter();
			// if(isFoundInsertLocation(template, templateDataModel,
			// absoluteOutputFilePath, newFileContentCollector)) {
			// System.out.println("[insert]\t generate content into:"+targetFilename);
			// IOHelper.saveFile(absoluteOutputFilePath,
			// newFileContentCollector.toString());
			// return;
			// }
			/*-----------------------------------------------------------------------------------------------------------------------*/
			/*
			 * 淇敼鍚庢槸濡傛灉瑕佺敓鎴愮殑鏂囦欢瀛樺湪,鍒欏垽鏂槸鍚︽湁鎻掑叆鏍囪瘑,濡傛灉鏈�鍒欐妸鏂囦欢鐢熸垚鍦ㄤ复鏃剁洰褰曢噷,鐒跺悗鍐嶆牴鎹
			 * 爣璇嗗鍘熸湁鏂囦欢杩涜鎻掑叆鍔ㄤ綔
			 */
			if (isFoundInsertLocation_v1(template, templateDataModel,
					absoluteOutputFilePath)) {

				File tempFile = new File(TEMPFILEPATH
						+ absoluteOutputFilePath.getName());
				if (!tempFile.exists()) {
					tempFile.createNewFile();
				}
				System.out.println("[insert]\t generate content into:"
						+ targetFilename);
				saveNewOutputFileContent(template, templateDataModel, tempFile);
				IOHelper.insertContent(absoluteOutputFilePath, tempFile,
						WEBAPP_GENERATOR_INSERT_BEGIN_LOCATION,
						WEBAPP_GENERATOR_INSERT_END_LOCATION);
				return;
			}
		} else {
			System.out.println("[generate]\t template:" + templateRelativePath
					+ " to " + targetFilename);
			saveNewOutputFileContent(template, templateDataModel,
					absoluteOutputFilePath);
		}
	}

	private String getTargetFilename(Table table, String templateFilepath)
			throws IllegalAccessException, InvocationTargetException,
			NoSuchMethodException {
		Map fileModel = getFilepathDataModel(table);
		String targetFilename = resolveFile(templateFilepath, fileModel);
		return targetFilename;
	}

	/**
	 * 寰楀埌鐢熸垚"鏂囦欢鐩綍/鏂囦欢璺緞"鐨凪odel
	 **/
	private Map getFilepathDataModel(Table table)
			throws IllegalAccessException, InvocationTargetException,
			NoSuchMethodException {
		Map fileModel = BeanUtils.describe(table);
		fileModel.putAll(PropertiesProvider.getProperties());
		return fileModel;
	}

	/**
	 * 寰楀埌FreeMarker鐨凪odel
	 **/
	private Map getTemplateDataModel(Table table) {
		Map model = new HashMap();
		model.putAll(PropertiesProvider.getProperties());
		model.put("table", table);
		return model;
	}

	private File getAbsoluteOutputFilePath(String targetFilename) {
		String outRoot = getOutRootDir();
		File outputFile = new File(outRoot, targetFilename);
		outputFile.getParentFile().mkdirs();
		return outputFile;
	}

	private boolean isFoundInsertLocation(Template template, Map model,
			File outputFile, StringWriter newFileContent) throws IOException,
			TemplateException {
		LineNumberReader reader = new LineNumberReader(new FileReader(
				outputFile));
		String line = null;
		boolean isFoundInsertLocation = false;

		PrintWriter writer = new PrintWriter(newFileContent);
		while ((line = reader.readLine()) != null) {
			writer.println(line);
			// only insert once
			if (!isFoundInsertLocation
					&& line.indexOf(WEBAPP_GENERATOR_INSERT_LOCATION) >= 0) {
				template.process(model, writer);
				writer.println();
				isFoundInsertLocation = true;
			}
		}

		writer.close();
		reader.close();
		return isFoundInsertLocation;
	}

	// 浣滆�:daniel
	private boolean isFoundInsertLocation_v1(Template template, Map model,
			File outputFile) throws IOException, TemplateException {
		LineNumberReader reader = new LineNumberReader(new FileReader(
				outputFile));
		String line = null;
		boolean isFoundInsertLocation = false;

		while ((line = reader.readLine()) != null) {
			// only insert once
			if (!isFoundInsertLocation
					&& line.indexOf(WEBAPP_GENERATOR_INSERT_LOCATION) >= 0) {
				isFoundInsertLocation = true;
				break;
			}
		}

		reader.close();
		return isFoundInsertLocation;
	}

	private void saveNewOutputFileContent(Template template, Map model,
			File outputFile) throws IOException, TemplateException {
		FileWriter out = new FileWriter(outputFile);
		template.process(model, out);
		out.close();
	}

	private String resolveFile(String templateFilepath, Map fileModel) {
		return new StringTemplate(templateFilepath, fileModel).toString();
	}

	public void clean() throws IOException {
		String outRoot = getOutRootDir();
		FileUtils.deleteDirectory(new File(outRoot));
		System.out.println("[Delete Dir]	" + outRoot);
	}

	private String getOutRootDir() {
		return PropertiesProvider.getProperties().getProperty("outRoot");
	}

}
